import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
// import {  Link } from "react-router-dom";
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { Button, Checkbox, Dropdown, Modal, Row, Col, Form, Radio, Input, Select, Tabs, message } from 'antd';
import copyToClipboard from 'copy-to-clipboard';

import 'antd/dist/reset.css';
import './header.scss';

import { getState, updateState } from '../ext/common/global_state';
import { getPlayTab } from '../ext/common/tab';
import { getPlayer, Player } from '../common/player';
import { hasUnsavedMacro } from '../recomputed';
import getSaveTestCase from './save_test_case';
import { Actions, Actions as simpleActions } from '../actions/simple_actions';
import * as actions from '../actions';
import * as C from '../common/constant';
import { range, setIn, updateIn, compose, cn } from '../common/utils';
import { getXFile } from '../services/xmodules/xfile';
import { getXLocal } from '../services/xmodules/xlocal';
import { getXUserIO } from '../services/xmodules/x_user_io';
import { getXDesktop } from '../services/xmodules/xdesktop';
import { getXScreenCapture } from '../services/xmodules/x_screen_capture';
import { getStorageManager, StorageManagerEvent } from '../services/storage';
import Ext from '../common/web_extension';
import { ocrLanguageOptions, tesseractLanguageOptions } from '../services/ocr/languages';
import { ocrViewport, ocrViewportCalibration } from '../init_player';
import { generateEmptyHtml } from '../common/convert_utils';
import { restoreBackup } from '../services/backup/restore';
import { parseProxyUrl } from '../services/proxy';
import ipc from '../common/ipc/ipc_cs';
import FileSaver from '../common/lib/file_saver';
import { encrypt } from '../common/encrypt';
import { getLicenseService } from '../services/license';
import { isNetworkError } from '../services/api/http_api';
import { Feature } from '../services/license/types';
import { isCVTypeForDesktop } from '../common/cv_utils';
import { SettingOutlined } from '@ant-design/icons';
import { isOcrSpaceFreeKey, testOcrSpaceAPIKey } from '../services/ocr';
import CONFIG from '@/config';
import { store } from '../redux';
import AITab from './settings_modal/tabs/ai';

const OSType = (() => {
    const ua = window.navigator.userAgent;
    if (/windows/i.test(ua)) return 'windows';
    if (/mac/i.test(ua)) return 'mac';
    return 'linux';
})();

function withRouter(Component) {
    function ComponentWithRouterProp(props) {
        let location = useLocation();
        let navigate = useNavigate();
        let params = useParams();
        return (
            <Component
                {...props}
                router={{ location, navigate, params }}
            />
        );
    }

    return ComponentWithRouterProp;
}

const applyPresetLicense = (registerKey) => {
    if (getLicenseService().isProLicense() || getLicenseService().isPersonalLicense()) {
        console.log('license already active.');
        return;
    }
    getLicenseService()
        .checkLicense(registerKey)
        .then((license) => {
            if (license.status === 'key_not_found') {
                console.error('License key not found');
            }
            console.log(`license status: ${license.status}`);
        })
        .catch((e) => {
            const text = isNetworkError(e) ? 'Internet connection required for activation. If you want use the software on a machine without Internet connection, please contact tech support' : e.message;
            console.error(text);
        });
};

class Header extends React.Component {
    state = {
        showPlayLoops: false,
        loopsStart: 1,
        loopsEnd: 3,
        xModules: [getXFile(), getXUserIO(), getXDesktop(), getXScreenCapture()],
        xModuleData: {},
        xModuleDataLocal: {},
        xFileRootDirChanged: false,
        registerKey: '',
        websiteWhiteListText: '',

        // Security Tab - Encrypt Text
        textToEncrypt: '',
        encryptedText: '',
        showText: false,

        isCheckingLicense: false,
        ocrLanguageOptions: this.props.config.ocrLanguageOption,
        tesseractLanguageOptions: tesseractLanguageOptions,

        userEnteredOCRAPIKey: '',
        connectedAPIEndpointType: null, // null | "free" | "pro"
    };

    getConnectedAPIEndpointType = (ocrSpaceApiKey) => {
        const apiEndpointType = ocrSpaceApiKey ? (isOcrSpaceFreeKey(ocrSpaceApiKey) ? 'free' : 'pro') : null;
        return Promise.resolve(apiEndpointType);
    };

    getPlayer = (name) => {
        if (name) return getPlayer({ name });

        switch (this.props.player.mode) {
            case C.PLAYER_MODE.TEST_CASE:
                return getPlayer({ name: 'testCase' });

            case C.PLAYER_MODE.TEST_SUITE:
                return getPlayer({ name: 'testSuite' });
        }
    };

    getTestCaseName = () => {
        const { src } = this.props.editing.meta;
        return src && src.name && src.name.length ? src.name : 'Untitled';
    };

    togglePlayLoopsModal = (toShow) => {
        this.setState({
            showPlayLoops: toShow,
        });
    };

    onToggleRecord = async () => {
        if (isCVTypeForDesktop(this.props.config.cvScope)) {
            const msg = 'Recording is only available for browser automation. Desktop automation macros are created by adding XClick and other visual commands step by step.';

            this.props.addLog('warning', msg);
            return message.warn(msg, 2.5);
        }

        const tabInfo = await this.getCurrentRecordedtab();
        if (!/^(https?:|file:)/.test(tabInfo.url)) {
            return message.error('Web recording works only on normal browser pages. For other pages, please use desktop automation.');
        }

        if (this.props.status === C.APP_STATUS.RECORDER) {
            this.props.stopRecording();
            // Note: remove targetOptions from all commands
            this.props.normalizeCommands();
        } else {
            console.log('startRecording:>> askPermission');
            const permissionResult = await this.askPermission();
            console.log('startRecording:>> askPermission complete: permissionResult:>>', permissionResult);
            if (!permissionResult) {
                return;
            }
            this.props.startRecording();
        }

        this.setState({ lastOperation: 'record' });
    };

    // Play loops relative
    onClickPlayLoops = async (isStep) => {
        const state = await getState();
        const bwindowId = state.tabIds.bwindowId;
        const wTab = bwindowId ? await this.checkWindowisOpen(bwindowId) : '';
        Ext.tabs.query({ active: true }).then((tabs) => {
            tabs = tabs.filter((tab) => tab.url !== Ext.runtime.getURL('popup.html'));
            const tab = (wTab ? wTab : tabs[0]) || {};
            updateState(setIn(['tabIds', 'toPlay'], tab.id));
            const { loopsStart, loopsEnd } = this.state;

            if (loopsStart < 0) {
                return message.error('Start value must be no less than zero', 1.5);
            }

            if (loopsEnd < loopsStart) {
                return message.error('Max value must be greater than start value', 1.5);
            }

            const player = this.getPlayer();
            const { commands } = this.props.editing;
            const { src } = this.props.editing.meta;
            const openTc = commands.find((tc) => tc.cmd.toLowerCase() === 'open' || tc.cmd.toLowerCase() === 'openbrowser');

            this.props.playerPlay({
                macroId: src && src.id,
                loopsEnd,
                loopsStart,
                title: this.getTestCaseName(),
                extra: {
                    id: src && src.id,
                },
                mode: player.C.MODE.LOOP,
                playUrl: tab.url,
                playtabIndex: tab.index,
                playtabId: tab.id,
                startIndex: 0,
                startUrl: openTc ? openTc.target : null,
                resources: this.props.editing.commands,
                postDelay: this.props.config.playCommandInterval * 1000,
            });

            this.setState({ lastOperation: 'play' });
            this.togglePlayLoopsModal(false);
        });
    };

    onCancelPlayLoops = () => {
        this.togglePlayLoopsModal(false);
        this.setState({
            loopsToPlay: 2,
        });
    };

    onChangePlayLoops = (field, value) => {
        this.setState({
            [field]: parseInt(value, 10),
        });
    };

    onClickSave = () => {
        return getSaveTestCase().save();
    };

    getCurrentRecordedtab = async () => {
        return new Promise((resolve, reject) => {
            Ext.tabs.query({ active: true }).then((tabs) => {
                tabs = tabs.filter((tab) => tab.url !== Ext.runtime.getURL('popup.html'));

                if (tabs.length !== 0) {
                    getPlayTab().then((tab) => {
                        resolve(tab);
                    });
                } else {
                    resolve(false);
                }
            });
        });
    };

    checkWindowisOpen = async (bwindowId) => {
        return new Promise((resolve, reject) => {
            chrome.tabs.query({}, function (tabs) {
                let doFlag = '';
                for (let i = tabs.length - 1; i >= 0; i--) {
                    if (tabs[i].windowId === bwindowId) {
                        doFlag = tabs[i];

                        break;
                    }
                }
                resolve(doFlag);
            });
        });
    };

    // firefox requires explicit permission to access all urls
    // ask user to grant permission, return promise
    askPermission = () => {
        // test code:
        // const permissions = chrome.runtime.getManifest().permissions || [];
        // console.log('permission:>> ', permissions)
        // let allUrlPermissions = {
        //   origins: ["<all_urls>"],
        //   permissions: ['activeTab', 'tabs']
        // };
        // return new Promise((resolve, reject) => {
        //   Ext.permissions.request(allUrlPermissions).then((result) => {
        //     console.log('permission result:>>', result)
        //     resolve(true)
        //   }).catch(e => {
        //     console.log('e:>>', e)
        //   })
        // })

        return new Promise((resolve, reject) => {
            if (Ext.isFirefox()) {
                Ext.permissions.contains({ origins: ['<all_urls>'] }).then((permissionGranted) => {
                    if (!permissionGranted) {
                        Modal.confirm({
                            title: 'Grant Permission To Replay Macros',
                            content: `RPA IDE is an open-source tool for automating tasks. To replay macros, it requires permission from Firefox to 'access data in all tabs'. If you click 'OK', RPA IDE will open the Firefox permission dialog, allowing you to provide this permission. Continue?`,
                            okText: '继续',
                            cancelText: '取消',
                            onOk: () => {
                                Ext.permissions.request({ origins: ['<all_urls>'] }).then((result) => {
                                    console.log('permission result:>>', result);
                                    if (result) {
                                        resolve(true);
                                    } else {
                                        // visit https://goto.ui.vision/x/idehelp?help=firefox_access_data_permission in new tab
                                        Ext.tabs.create({
                                            url: '',
                                            active: true,
                                        });
                                        resolve(false);
                                    }
                                });
                            },
                            onCancel: () => {
                                // visit https://goto.ui.vision/x/idehelp?help=firefox_access_data_permission in new tab
                                Ext.tabs.create({
                                    url: '',
                                    active: true,
                                });

                                resolve(false);
                            },
                        });
                    } else {
                        resolve(true);
                    }
                });
            } else {
                resolve(true);
            }
        });
    };
    // 运行当前用例
    playCurrentMacro = async (isStep) => {
        const permissionResult = await this.askPermission();
        if (!permissionResult) {
            return;
        }

        Ext.tabs.query({ active: true }).then(async (tabs) => {
            const { commands } = this.props.editing;
            const { src } = this.props.editing.meta;
            const openTc = commands.find((tc) => tc.cmd.toLowerCase() === 'open' || tc.cmd.toLowerCase() === 'openbrowser');

            tabs = tabs.filter((tab) => tab.url !== Ext.runtime.getURL('popup.html'));

            const state = await getState();
            const bwindowId = state.tabIds.bwindowId;
            const wTab = bwindowId ? await this.checkWindowisOpen(bwindowId) : '';
            const tab = (wTab ? wTab : tabs[0]) || {};
            updateState(setIn(['tabIds', 'toPlay'], tab.id));
            this.setState({ lastOperation: 'play' });
            console.log('playCurrentMacro-1');
            this.props.playerPlay({
                macroId: src && src.id,
                title: this.getTestCaseName(),
                extra: {
                    id: src && src.id,
                },
                mode: getPlayer().C.MODE.STRAIGHT,
                playUrl: tab.url,
                playtabIndex: tab.index,
                playtabId: tab.id,
                startIndex: 0,
                startUrl: openTc ? openTc.target : null,
                resources: commands,
                postDelay: this.props.config.playCommandInterval * 1000,
                isStep: isStep,
                superFast: false,
                hasOnDownloadCmd: false,
            });
        });
    };

    playCurrentLine = () => {
        const { commands } = this.props.editing;
        const { src, selectedIndex } = this.props.editing.meta;
        const commandIndex = selectedIndex === -1 ? 0 : selectedIndex || 0;

        return this.props.playerPlay({
            macroId: src && src.id,
            title: this.getTestCaseName(),
            extra: {
                id: src && src.id,
            },
            mode: Player.C.MODE.SINGLE,
            startIndex: commandIndex,
            startUrl: null,
            resources: commands,
            postDelay: this.props.config.playCommandInterval * 1000,
            callback: (err, res) => {
                if (err) return;

                // Note: auto select next command
                if (commandIndex + 1 < commands.length) {
                    this.props.selectCommand(commandIndex + 1, true);
                }
            },
        });
    };

    checkRegisterKey = () => {
        const { registerKey } = this.state;
        const checkBasicPattern = (str) => {
            return str.length === 15 && str.charAt(0) === 'K';
        };
        const checkUnregistered = (str) => str === 'freeman';

        if (checkUnregistered(registerKey)) {
            this.props.updateConfig({ xmodulesStatus: 'unregistered' });
            message.success(`Unregistered`);
            getStorageManager().emit(StorageManagerEvent.RootDirChanged);
            this.resetRegisterKey();
            this.forceUpdate();
            return;
        }

        const notifyLicenseError = () => message.error('Invalid license key');

        if (!checkBasicPattern(registerKey)) {
            return notifyLicenseError();
        }

        this.setState({ isCheckingLicense: true });

        return getLicenseService()
            .checkLicense(registerKey)
            .then((license) => {
                if (license.status === 'key_not_found') {
                    return notifyLicenseError();
                }

                this.resetRegisterKey();
                this.forceUpdate();
                getStorageManager().emit(StorageManagerEvent.RootDirChanged);
                message.success('License key verified');
            })
            .catch((e) => {
                const text = isNetworkError(e) ? 'Internet connection required for activation. If you want use the software on a machine without Internet connection, please contact tech support' : e.message;

                message.error(text, 4);
            })
            .finally(() => {
                this.setState({ isCheckingLicense: false });
            });
    };

    beforeUnloadHandler = (event) => {
        const { hasUnsaved } = this.props;
        if (hasUnsaved) {
            // Note: Chrome is showing the default message anyway
            const promptMessage = 'You have unsaved Changes. Do you want to save before leaving application?';
            event.returnValue = promptMessage;
            return promptMessage;
        }
    };

    componentDidMount() {
        const { location, navigate, params } = this.props.router;

        this.props.setRoute(location.pathname);
        // TODO: may require to fix this
        // this.props.history.listen((location, action) => {
        //   this.props.setRoute(location.pathname)
        // })

        getLicenseService().getLatestInfo((info) => {
            this.setState({
                licenseInfo: info,
            });
        });

        this.getConnectedAPIEndpointType(this.props.config.ocrSpaceApiKey).then((apiEndpointType) => {
            this.setState({
                connectedAPIEndpointType: apiEndpointType,
            });
        });

        // preset #210
        // uncomment the following line to activate it
        // applyPresetLicense('LICENSE KEY HERE')

        window.addEventListener('beforeunload', this.beforeUnloadHandler);
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.ui.showSettings && !this.props.ui.showSettings) {
            this.onShowSettings();
        }

        if (nextProps.ui.showWebsiteWhiteList && !this.props.ui.showWebsiteWhiteList) {
            this.setState({
                websiteWhiteListText: (this.props.config.websiteWhiteList || []).join('\n'),
            });
        }
    }

    initLocalXmodule() {
        getXLocal()
            .getVersionLocal()
            .then((data) => {
                const { installed, version } = data;
                const p = !installed ? Promise.resolve() : getXLocal().initConfig();
                p.catch((e) => {}).then(() => {
                    this.setState(
                        updateIn(
                            ['xModuleDataLocal', getXLocal().getName()],
                            (orig) => ({
                                ...orig,
                                ...data,
                                config: getXLocal().getCachedConfig(),
                            }),
                            this.state
                        )
                    );
                });
            });
    }
    initXModules() {
        const xModules = this.state.xModules;

        // versionInfo: {
        //  installed: boolean
        //  version: string
        // },
        // checkResult: {
        //  error: string | null
        // }
        Promise.all(
            xModules.map((mod) => {
                // Note: call init config for each xmodule and discard any error
                return mod
                    .initConfig()
                    .catch((e) => {})
                    .then(() => mod.getVersion())
                    .then((versionInfo) => {
                        if (versionInfo.installed) {
                            return mod
                                .sanityCheck()
                                .then(
                                    () => ({ error: null }),
                                    (e) => ({ error: e.message })
                                )
                                .then((checkResult) => ({
                                    versionInfo,
                                    checkResult,
                                }));
                        } else {
                            return {
                                versionInfo,
                                checkResult: null,
                            };
                        }
                    });
            })
        ).then((results) => {
            const xModuleData = results.reduce((prev, r, i) => {
                prev[xModules[i].getName()] = {
                    ...r.versionInfo,
                    checkResult: r.checkResult,
                    config: xModules[i].getCachedConfig(),
                };
                return prev;
            }, {});

            getXFile()
                .getVersion()
                .then((data) => {
                    const { installed, version } = data;

                    if (xModuleData.xFile !== undefined) {
                        xModuleData.xFile.installed = installed;
                        xModuleData.xFile.version = version;
                    }
                    this.setState({
                        xModuleData,
                        xFileRootDirChanged: false,
                    });
                });
        });
    }

    isEitherXModuleInstalled() {
        const xFileData = this.state.xModuleData[getXFile().getName()];
        const xUserIOData = this.state.xModuleData[getXUserIO().getName()];

        return (xFileData && xFileData.installed) || (xUserIOData && xUserIOData.installed);
    }

    resetRegisterKey() {
        this.setState({
            registerKey: '',
        });
    }

    onShowSettings() {
        this.initXModules();
        this.initLocalXmodule();
        this.resetRegisterKey();
    }

    showSettingsModal() {
        this.props.updateUI({ showSettings: true });
    }

    showSettingsOfflineModal() {
        this.props.updateUI({ showSettingsOffline: true });
    }
    // 白名单
    renderPublicWebsiteWhiteList() {
        return (
            <Modal
                title='嵌入式用例网站白名单'
                className='whitelist-modal'
                width={450}
                okText='Save'
                open={this.props.ui.showWebsiteWhiteList}
                onCancel={() => this.props.updateUI({ showWebsiteWhiteList: false })}
                onOk={(close) => {
                    const text = this.state.websiteWhiteListText;
                    const lines = text
                        .split(/\n/g)
                        .map((str) => str.trim())
                        .filter((str) => str.length > 0);

                    this.props.updateConfig({ websiteWhiteList: lines });
                    this.props.updateUI({ showWebsiteWhiteList: false });
                    message.success('Saved');

                    return Promise.resolve(true);
                }}
            >
                <p style={{ marginBottom: '10px' }}>
                    Allow embedded macros to run <em>without warning dialog</em>, if started from the following sites:
                </p>
                <Input.TextArea
                    placeholder='每行填写一个url, 例如：https://ui.vision/rpa'
                    autosize={{ minRows: 6, maxRows: 12 }}
                    value={this.state.websiteWhiteListText}
                    style={{ resize: 'vertical' }}
                    onChange={(e) => this.setState({ websiteWhiteListText: e.target.value })}
                />
                <p style={{ color: 'green', marginTop: '20px' }}>
                    {/* <a
                        style={{ float: 'right', marginLeft: '20px' }}
                         href='https://goto.ui.vision/x/idehelp?help=website_whitelist'
                        target='_blank'
                    >
                        更多信息
                    </a> */}
                    仅运行来自您信任网站的嵌入式用例
                </p>
            </Modal>
        );
    }
    // 设置循环次数
    renderPlayLoopModal() {
        return (
            <Modal
                title='运行多少次循环？'
                okText='运行'
                cancelText='取消'
                className='play-loop-modal'
                open={this.state.showPlayLoops}
                onOk={this.onClickPlayLoops}
                onCancel={this.onCancelPlayLoops}
            >
                <Row>
                    <Col span={10}>
                        <Form.Item label='开始值'>
                            <Input
                                type='number'
                                min='0'
                                value={this.state.loopsStart}
                                onKeyDown={(e) => {
                                    if (e.keyCode === 13) this.onClickPlayLoops();
                                }}
                                onChange={(e) => this.onChangePlayLoops('loopsStart', e.target.value)}
                            />
                        </Form.Item>
                    </Col>
                    <Col
                        span={10}
                        offset={2}
                    >
                        <Form.Item label='结束值'>
                            <Input
                                type='number'
                                min='0'
                                value={this.state.loopsEnd}
                                onKeyDown={(e) => {
                                    if (e.keyCode === 13) this.onClickPlayLoops();
                                }}
                                onChange={(e) => this.onChangePlayLoops('loopsEnd', e.target.value)}
                            />
                        </Form.Item>
                    </Col>
                </Row>

                <p>
                    循环计数器的值在 ${'{'}!LOOP{'}'} 变量中可用
                </p>
            </Modal>
        );
    }

    renderSettingOfflineModal() {
        return (
            <Modal
                title='企业OCR服务器'
                className='settings-modal'
                width={650}
                footer={null}
                open={this.props.ui.showSettingsOffline}
                onCancel={() => {
                    this.props.updateUI({ showSettingsOffline: false });
                }}
            >
                <div className='row'>
                    <Radio.Group
                        className='radio-block'
                        value={this.props.config.ocrMode}
                    >
                        <Radio
                            value='offline_enabled'
                            onClick={() => {
                                onConfigChange('ocrMode', 'offline_enabled');
                            }}
                            disabled={!getLicenseService().isProLicense()}
                            className={cn({
                                'need-pro': !getLicenseService().isProLicense(),
                            })}
                        >
                            Use
                            <a
                                // href='https://goto.ui.vision/x/idehelp?help=ocrenterprise'
                                target='_blank'
                            >
                                本地企业OCR服务器
                            </a>
                            (需要升级为XModules企业版)
                            <br />
                            <div className='row offline-modal-row'>
                                <span className='offline-modal-label'>本地 OCR</span>
                                <Input
                                    type='text'
                                    style={{ width: '200px' }}
                                    disabled={this.props.config.ocrMode !== 'offline_enabled'}
                                    value={this.props.config.ocrOfflineURL}
                                    onChange={(e) => onConfigChange('ocrOfflineURL', e.target.value)}
                                />
                                <br />
                                <span className='offline-modal-label'>本地 API 密钥</span>
                                <Input
                                    type='password'
                                    style={{ width: '200px' }}
                                    disabled={this.props.config.ocrMode !== 'offline_enabled'}
                                    value={this.props.config.ocrOfflineAPIKey}
                                    onChange={(e) => onConfigChange('ocrOfflineAPIKey', e.target.value)}
                                />
                            </div>
                        </Radio>
                    </Radio.Group>
                </div>
            </Modal>
        );
    }

    renderSettingModal() {
        const onConfigChange = (key, val) => {
            this.props.updateConfig({ [key]: val });
        };

        const onChangeProxyStatus = (value) => {
            switch (value) {
                case 'off':
                    return ipc.ask('PANEL_SET_PROXY', { proxy: null });

                case 'on': {
                    let proxy;

                    try {
                        proxy = parseProxyUrl(this.props.config.defaultProxy, this.props.config.defaultProxyAuth);
                    } catch (e) {
                        return message.error(e.message);
                    }

                    return ipc.ask('PANEL_SET_PROXY', { proxy });
                }
            }
        };

        const onChangeDefaultOCREngine = (value) => {
            const lastSelectedEngine = this.props.config.ocrEngine;
            onConfigChange('ocrEngine', parseInt(value, 10));
            if (value === '99') {
                if (OSType === 'linux') {
                    const msg = 'Local OCR not supported on Linux yet';
                    message.warn(`${msg}`, 2.5);
                    onConfigChange('ocrEngine', 98); // set default. // old: parseInt(1, 10));
                } else {
                    getXFile()
                        .getLangs(OSType)
                        .then(
                            (data) => {
                                if (data) {
                                    const options = JSON.parse(atob(data));
                                    console.log('getXFile options:>>', options);
                                    // output: getXFile options:>> ['eng']
                                    let newOcrlangAr = [];
                                    this.state.ocrLanguageOptions.map((item) =>
                                        options.indexOf(item.value) > -1
                                            ? newOcrlangAr.push({
                                                  text: item.text,
                                                  value: item.value,
                                              })
                                            : []
                                    );

                                    this.setState({
                                        ocrLanguageOptions: newOcrlangAr,
                                    });
                                    onConfigChange('ocrLanguageOption', newOcrlangAr);
                                    let haveEng = newOcrlangAr.filter((lang) => lang.value === 'eng');
                                    if (haveEng.length !== 0) {
                                        onConfigChange('ocrLanguage', 'eng');
                                    } else {
                                        onConfigChange('ocrLanguage', newOcrlangAr[0]['value']);
                                    }
                                } else {
                                    const msg = '未安装';
                                    message.info(`status updated: ${msg}`);
                                }
                            },
                            () => {
                                this.setState({
                                    ocrLanguageOptions: this.state.ocrLanguageOptions,
                                });
                                onConfigChange('ocrLanguage', 'eng');
                                onConfigChange('ocrLanguageOption', this.state.ocrLanguageOptions);
                                const msg = '未安装';
                                onConfigChange('ocrEngine', lastSelectedEngine);
                                message.info(`status updated: ${msg}`);
                            }
                        );
                }
            } else if (value === '98') {
                let tesseractLangAr = this.state.tesseractLanguageOptions.map((item) => {
                    return {
                        text: item.text,
                        value: item.value,
                    };
                });

                this.setState({
                    tesseractLanguageOptions: tesseractLangAr,
                });

                // onConfigChange("tesseractLanguageOption", tesseractLangAr);
                let haveEng = tesseractLangAr.filter((lang) => lang.value === 'eng');
                if (haveEng.length !== 0) {
                    onConfigChange('ocrLanguage', 'eng');
                } else {
                    onConfigChange('ocrLanguage', tesseractLangAr[0]['value']);
                }
            } else {
                this.setState({ ocrLanguageOptions: ocrLanguageOptions });
                onConfigChange('ocrLanguageOption', ocrLanguageOptions);
                onConfigChange('ocrLanguage', 'eng');
            }
        };

        const displayConfig = {
            labelCol: { span: 8 },
            wrapperCol: { span: 16 },
        };

        const ocrClassName =
            cn('ocr-pane', {
                'ocr-disabled': this.props.config.ocrMode === 'disabled',
                'ocr-enabled': this.props.config.ocrMode === 'enabled',
                'ocr-offline': this.props.config.ocrMode === 'offline_enabled',
            }) || '';

        return (
            <Modal
                title='设置'
                className='settings-modal'
                width={700}
                footer={null}
                open={this.props.ui.showSettings}
                onCancel={() => {
                    this.props.updateUI({ showSettings: false });
                    this.setState({ textToEncrypt: '', encryptedText: '' });
                    this.props.updateConfig({
                        showSettingsOnStart: false,
                    });
                    this.setState({ userEnteredOCRAPIKey: '' });
                }}
            >
                <Tabs
                    type='card'
                    activeKey={this.props.ui.settingsTab || 'replay'}
                    onChange={(activeKey) => this.props.updateUI({ settingsTab: activeKey })}
                    items={[
                        {
                            key: 'replay',
                            label: '录制/运行',
                            children: (
                                <>
                                    <Form>
                                        <Form.Item
                                            label='录制通知'
                                            {...displayConfig}
                                        >
                                            <Checkbox
                                                onClick={(e) => onConfigChange('recordNotification', !e.target.checked)}
                                                checked={this.props.config.recordNotification}
                                            >
                                                录制时显示通知
                                            </Checkbox>
                                        </Form.Item>
                                    </Form>
                                    <Form>
                                        <Form.Item
                                            label='运行助手'
                                            {...displayConfig}
                                        >
                                            <Checkbox
                                                onClick={(e) => onConfigChange('recordHighlightElements', !e.target.checked)}
                                                checked={this.props.config.recordHighlightElements}
                                            >
                                                录制时高亮显示元素
                                            </Checkbox>
                                            <Checkbox
                                                onClick={(e) => onConfigChange('playScrollElementsIntoView', !e.target.checked)}
                                                checked={this.props.config.playScrollElementsIntoView}
                                            >
                                                运行时滚动元素到可视区域
                                            </Checkbox>

                                            <Checkbox
                                                onClick={(e) => onConfigChange('playHighlightElements', !e.target.checked)}
                                                checked={this.props.config.playHighlightElements}
                                            >
                                                运行时高亮显示元素
                                            </Checkbox>
                                            <Checkbox
                                                onClick={(e) => onConfigChange('playOnNewWIndow', !e.target.checked)}
                                                checked={this.props.config.playOnNewWIndow}
                                            >
                                                使用新窗口运行
                                            </Checkbox>
                                        </Form.Item>

                                        <Form.Item
                                            label='命令间隔'
                                            {...displayConfig}
                                        >
                                            <Select
                                                style={{ width: '200px' }}
                                                placeholder='间隔'
                                                value={'' + this.props.config.playCommandInterval}
                                                onChange={(val) => onConfigChange('playCommandInterval', val)}
                                            >
                                                <Select.Option value={'0'}>快 (无延迟)</Select.Option>
                                                <Select.Option value={'0.3'}>中 (0.3秒 延迟)</Select.Option>
                                                <Select.Option value={'2'}>慢 (2秒 延迟)</Select.Option>
                                            </Select>
                                        </Form.Item>

                                        <Form.Item
                                            label='页面加载超时'
                                            {...displayConfig}
                                        >
                                            <Input
                                                type='number'
                                                min='0'
                                                style={{ width: '70px' }}
                                                value={this.props.config.timeoutPageLoad}
                                                onChange={(e) => onConfigChange('timeoutPageLoad', e.target.value)}
                                                placeholder='以秒为单位'
                                            />
                                            <span className='tip'>新页面加载的最长时间</span>
                                        </Form.Item>
                                        <Form.Item
                                            label='单步超时'
                                            {...displayConfig}
                                        >
                                            <Input
                                                type='number'
                                                min='0'
                                                style={{ width: '70px' }}
                                                value={this.props.config.timeoutElement}
                                                onChange={(e) => onConfigChange('timeoutElement', e.target.value)}
                                                placeholder='以秒为单位'
                                            />
                                            <span className='tip'>单步最长运行时间</span>
                                        </Form.Item>
                                        <Form.Item
                                            label='用例执行超时'
                                            {...displayConfig}
                                        >
                                            <Input
                                                type='number'
                                                min='0'
                                                style={{ width: '70px' }}
                                                value={this.props.config.timeoutMacro}
                                                onChange={(e) => onConfigChange('timeoutMacro', e.target.value)}
                                                placeholder='以秒为单位'
                                            />
                                            <span className='tip'>用例最大总运行时间</span>
                                        </Form.Item>
                                        <Form.Item
                                            label='下载超时'
                                            {...displayConfig}
                                        >
                                            <Input
                                                type='number'
                                                min='0'
                                                style={{ width: '70px' }}
                                                value={this.props.config.timeoutDownload}
                                                onChange={(e) => onConfigChange('timeoutDownload', e.target.value)}
                                                placeholder='以秒为单位'
                                            />
                                            <span className='tip'>文件下载最大允许时间</span>
                                        </Form.Item>
                                        <Form.Item
                                            label='如果循环中发生错误'
                                            {...displayConfig}
                                        >
                                            <Radio.Group value={this.props.config.onErrorInLoop}>
                                                <Radio
                                                    onClick={(e) => onConfigChange('onErrorInLoop', 'continue_next_loop')}
                                                    value='continue_next_loop'
                                                >
                                                    继续下一个循环
                                                </Radio>
                                                <Radio
                                                    onClick={(e) => onConfigChange('onErrorInLoop', 'stop')}
                                                    value='stop'
                                                >
                                                    停止
                                                </Radio>
                                            </Radio.Group>
                                        </Form.Item>
                                        <Form.Item
                                            label='侧边栏'
                                            {...displayConfig}
                                        >
                                            <Checkbox
                                                onClick={(e) => {
                                                    onConfigChange('showSidePanel', !e.target.checked);
                                                }}
                                                checked={this.props.config.showSidePanel}
                                            >
                                                默认打开侧边栏
                                            </Checkbox>
                                            <Checkbox
                                                onClick={(e) => {
                                                    onConfigChange('sidePanelOnLeft', !e.target.checked);
                                                }}
                                                checked={this.props.config.sidePanelOnLeft}
                                            >
                                                检查侧边栏是否在左侧
                                                {/* (
                                                <a
                                                    onClick={(e) => {
                                                        e.preventDefault();
                                                        e.stopPropagation();
                                                        window.open('https://goto.ui.vision/x/idehelp?help=sidepanel_left');
                                                    }}
                                                >
                                                    更多信息
                                                </a>
                                                ) */}
                                            </Checkbox>
                                        </Form.Item>
                                        <Form.Item
                                            label='颜色主题'
                                            {...displayConfig}
                                        >
                                            <Checkbox
                                                onClick={(e) => {
                                                    const useDarkTheme = !e.target.checked;
                                                    onConfigChange('useDarkTheme', !e.target.checked);
                                                    if (useDarkTheme) {
                                                        document.documentElement.setAttribute('data-theme', 'dark');
                                                    } else {
                                                        document.documentElement.setAttribute('data-theme', 'light');
                                                    }
                                                }}
                                                checked={this.props.config.useDarkTheme}
                                                style={{ marginBottom: 0 }}
                                            >
                                                使用暗黑模式 (
                                                <a
                                                    onClick={(e) => {
                                                        e.preventDefault();
                                                        e.stopPropagation();
                                                        // window.open('https://goto.ui.vision/x/idehelp?help=darkmode');
                                                    }}
                                                >
                                                    测试版-请点击此处反馈问题
                                                </a>
                                                )
                                            </Checkbox>
                                        </Form.Item>
                                    </Form>
                                </>
                            ),
                        },
                        {
                            key: 'api',
                            label: 'API',
                            className: 'api-pane',
                            children: (
                                <>
                                    <p>
                                        RPA 命令行 API 允许您从命令行运行用例和用例列表，并使用任何脚本或编程语言控制 RPA IDE
                                        {/* (
                                        <a
                                             href='https://goto.ui.vision/x/idehelp?help=cmdline'
                                            target='_blank'
                                        >
                                            更多信息
                                        </a>
                                        ) */}
                                    </p>

                                    <p>
                                        <Button
                                            type='primary'
                                            onClick={() => {
                                                const str = generateEmptyHtml();
                                                const blob = new Blob([str], {
                                                    type: 'text/plain;charset=utf-8',
                                                });

                                                FileSaver.saveAs(blob, `rpa.ide.html`, true);
                                            }}
                                        >
                                            生成自启动HTML页面
                                        </Button>
                                    </p>

                                    <Form>
                                        <Form.Item
                                            label={
                                                <a
                                                    target='_blank'
                                                    // href='https://goto.ui.vision/x/idehelp?help=cmdline'
                                                >
                                                    允许命令行
                                                </a>
                                            }
                                            {...displayConfig}
                                            labelCol={{ span: 6 }}
                                        >
                                            <Checkbox
                                                onClick={(e) => onConfigChange('allowRunFromBookmark', !e.target.checked)}
                                                checked={this.props.config.allowRunFromBookmark}
                                            >
                                                从Javascript书签运行用例
                                            </Checkbox>
                                            <Checkbox
                                                onClick={(e) => onConfigChange('allowRunFromFileSchema', !e.target.checked)}
                                                checked={this.props.config.allowRunFromFileSchema}
                                            >
                                                从本地文件运行嵌入式用例
                                            </Checkbox>
                                            <Checkbox
                                                onClick={(e) => onConfigChange('allowRunFromHttpSchema', !e.target.checked)}
                                                checked={this.props.config.allowRunFromHttpSchema}
                                            >
                                                从公共网站运行嵌入式用例
                                                <a
                                                    href='#'
                                                    style={{
                                                        position: 'relative',
                                                        marginLeft: '10px',
                                                        padding: '15px 0',
                                                    }}
                                                    onClick={(e) => {
                                                        e.stopPropagation();
                                                        e.preventDefault();
                                                        this.props.updateUI({ showWebsiteWhiteList: true });
                                                    }}
                                                >
                                                    编辑白名单
                                                </a>
                                            </Checkbox>
                                        </Form.Item>
                                    </Form>
                                </>
                            ),
                        },
                        {
                            key: 'ocr',
                            label: 'OCR',
                            className: ocrClassName,
                            children: (
                                <>
                                    <div>
                                        <p>
                                            <span className='label-text'>选择默认OCR引擎</span>
                                        </p>
                                    </div>
                                    <div>
                                        <span className='label-text'>
                                            本地 OCR:
                                            {/* <a
                                                 href='https://goto.ui.vision/x/idehelp?help=ocr-local'
                                                target='_blank'
                                            >
                                                更多信息
                                            </a> */}
                                        </span>
                                        <br />
                                        <Radio.Group
                                            className='radio-block'
                                            style={{ marginLeft: '5%' }}
                                            value={'' + this.props.config.ocrEngine}
                                        >
                                            <Radio
                                                value='98'
                                                onClick={() => onChangeDefaultOCREngine('98')}
                                            >
                                                Javascript 本地 OCR（适用于多种用例，可根据需要提供其他
                                                <a
                                                    href='https://goto.ui.vision/x/idehelp?help=ocr-request'
                                                    target='_blank'
                                                >
                                                    OCR 语言
                                                </a>
                                                ）
                                            </Radio>

                                            <Radio
                                                value='99'
                                                onClick={() => onChangeDefaultOCREngine('99')}
                                            >
                                                XModule 本地 OCR（更快/更好，特别是对于图像上的文本）
                                            </Radio>
                                        </Radio.Group>
                                    </div>
                                    <div className='row'>
                                        <span className='label-text'>
                                            在线OCR Ocr.Space：
                                            {'   ['}
                                            <a
                                                href='https://goto.ui.vision/x/idehelp?help=free-ocr-api'
                                                target='_blank'
                                            >
                                                需要OCR API免费帐户
                                            </a>
                                            {']'}
                                        </span>
                                        <br />
                                        <Radio.Group
                                            className='radio-block'
                                            style={{ marginLeft: '5%' }}
                                            value={'' + this.props.config.ocrEngine}
                                        >
                                            <Radio
                                                value='1'
                                                onClick={() => onChangeDefaultOCREngine('1')}
                                            >
                                                OCR.Space 引擎1
                                            </Radio>
                                            <Radio
                                                value='2'
                                                onClick={() => onChangeDefaultOCREngine('2')}
                                            >
                                                OCR.Space 引擎2
                                            </Radio>
                                        </Radio.Group>
                                        <div>
                                            <span className='label-text'>OCR.Space OCR API 密钥:</span>
                                            <Input
                                                type='text'
                                                style={{ width: '120px' }}
                                                value={this.state.userEnteredOCRAPIKey || 'K83341710688957'}
                                                disabled={[1, 2].includes(this.props.config.ocrEngine) ? false : true}
                                                onChange={(e) => {
                                                    this.setState({
                                                        userEnteredOCRAPIKey: e.target.value,
                                                    });
                                                }}
                                            />
                                            <Button
                                                type='primary'
                                                style={{ marginLeft: '8px' }}
                                                disabled={[1, 2].includes(this.props.config.ocrEngine) ? false : true}
                                                onClick={() => {
                                                    // connect to endpoint
                                                    let key = this.state.userEnteredOCRAPIKey?.trim();
                                                    if (!key) {
                                                        message.error('请输入有效的 API 密钥');
                                                        return;
                                                    }
                                                    const isFreeApiKey = isOcrSpaceFreeKey(key);
                                                    let url;

                                                    if (!isFreeApiKey) {
                                                        // it's a pro key
                                                        url = this.props.config.ocrEngine === 1 ? CONFIG.ocr.proApi1Endpoint : CONFIG.ocr.proApi2Endpoint;
                                                    } else {
                                                        url = CONFIG.ocr.freeApiEndpoint;
                                                    }

                                                    testOcrSpaceAPIKey({ key, url })
                                                        .then((res) => {
                                                            if (res) {
                                                                let endpointType = isFreeApiKey ? 'free' : 'pro';
                                                                this.setState({
                                                                    connectedAPIEndpointType: endpointType,
                                                                });
                                                                onConfigChange('ocrSpaceApiKey', key);
                                                            } else {
                                                                message.error('Invalid API key');
                                                                this.setState({
                                                                    connectedAPIEndpointType: null,
                                                                });
                                                                onConfigChange('ocrSpaceApiKey', '');
                                                            }
                                                        })
                                                        .catch((e) => {
                                                            message.error(e.message);
                                                        });
                                                }}
                                            >
                                                Test
                                            </Button>
                                            {this.state.connectedAPIEndpointType ? <span className='api-key-notification'>API key stored. Connected to {this.state.connectedAPIEndpointType.toUpperCase()} endpoint.</span> : null}
                                        </div>
                                    </div>

                                    <div className='row'>
                                        <div>
                                            <span className='label-text'>默认OCR语言</span>
                                            <Select
                                                id='ss'
                                                style={{ width: '150px' }}
                                                placeholder='OCR 语言'
                                                value={this.props.config.ocrLanguage}
                                                disabled={(this.props.config.ocrMode === 'disabled' || this.props.config.ocrEngine === 2) && this.props.config.ocrEngine !== 99}
                                                onChange={(val) => onConfigChange('ocrLanguage', val)}
                                            >
                                                {this.props.config.ocrEngine === 98
                                                    ? this.state.tesseractLanguageOptions.map((item) => (
                                                          <Select.Option
                                                              value={item.value}
                                                              key={item.value}
                                                          >
                                                              {item.text}
                                                          </Select.Option>
                                                      ))
                                                    : this.state.ocrLanguageOptions.map((item) => (
                                                          <Select.Option
                                                              value={item.value}
                                                              key={item.value}
                                                          >
                                                              {item.text}
                                                          </Select.Option>
                                                      ))}
                                            </Select>
                                        </div>

                                        <div>
                                            您可以使用内部变量
                                            <a
                                                href='https://goto.ui.vision/x/idehelp?help=ocrlanguage'
                                                target='_blank'
                                            >
                                                !OCRLanguage
                                            </a>
                                            和
                                            <a
                                                href='https://goto.ui.vision/x/idehelp?help=ocrengine'
                                                target='_blank'
                                            >
                                                !OCREngine
                                            </a>
                                            覆盖用例中的默认OCR设置。
                                        </div>
                                    </div>
                                    <div className='row'>
                                        <p>
                                            <Button
                                                type='primary'
                                                loading={this.state.testingOcrAPI}
                                                disabled={this.props.config.ocrMode === 'disabled' && this.props.config.ocrEngine !== 99 && this.props.config.ocrEngine !== 98}
                                                onClick={() => {
                                                    this.setState({ testingOcrAPI: true });

                                                    const isDesktopMode = isCVTypeForDesktop(this.props.config.cvScope);
                                                    isDesktopMode && store.dispatch(Actions.setOcrInDesktopMode(true));

                                                    ocrViewport({
                                                        store: window['store'],
                                                        isDesktop: isDesktopMode,
                                                    })
                                                        .catch((e) => {
                                                            message.error(e.message);
                                                        })
                                                        .then(() => {
                                                            this.setState({ testingOcrAPI: false });
                                                            store.dispatch(Actions.setOcrInDesktopMode(false));
                                                        });
                                                }}
                                            >
                                                显示 OCR 覆盖层
                                            </Button>
                                        </p>
                                        <p>测试在当前活动的浏览器选项卡上运行 OCR，并将结果显示为覆盖层。</p>
                                    </div>
                                    <div className='row'>
                                        <p>
                                            <b>
                                                校准 - <span className='label-text'>OCRTEXTX</span>
                                            </b>
                                            <Input
                                                type='number'
                                                min={1}
                                                value={this.props.config.ocrCalibration !== undefined && this.props.config.ocrCalibration !== '' ? this.props.config.ocrCalibration : 6}
                                                onChange={(e) => {
                                                    onConfigChange('ocrCalibration', e.target.value);
                                                    onConfigChange('ocrCalibration_internal', e.target.value);
                                                    //localStorage.setItem('ocrCalibration', e.target.value);
                                                }}
                                                style={{ width: '65px', marginRight: '15px' }}
                                            />
                                            <Button
                                                type='primary'
                                                loading={this.state.testingCalibrate}
                                                disabled={this.props.config.ocrMode === 'disabled' && this.props.config.ocrEngine !== 99}
                                                onClick={() => {
                                                    this.setState({
                                                        testingCalibrate: true,
                                                    });
                                                    ocrViewportCalibration({
                                                        store: window['store'],
                                                        isDesktop: true,
                                                    })
                                                        .catch((e) => {
                                                            message.error(e.message);
                                                        })
                                                        .then(async (result) => {
                                                            try {
                                                                let calibrateNumber = parseInt(store.getState().config.ocrCalibration_internal);
                                                                localStorage.setItem('ocrCalibration', calibrateNumber);
                                                                onConfigChange('ocrCalibration', calibrateNumber);
                                                                onConfigChange('ocrCalibration_internal', calibrateNumber);
                                                            } catch (e) {}

                                                            this.setState({
                                                                testingCalibrate: false,
                                                            });
                                                        });
                                                }}
                                            >
                                                校准 XClickTextRelative
                                            </Button>
                                            (
                                            <a
                                                href='https://goto.ui.vision/x/idehelp?help=ocr-calibrate-textrelative'
                                                target='_blank'
                                            >
                                                这是什么？
                                            </a>
                                            )
                                        </p>
                                    </div>
                                    <div className='row'>
                                        <p>
                                            <b>
                                                <span className='label-text'>屏幕缩放 %：</span>
                                            </b>
                                            <Input
                                                type='number'
                                                min={100}
                                                value={this.props.config.ocrScaling !== undefined && this.props.config.ocrScaling !== '' ? this.props.config.ocrScaling : 100}
                                                onChange={(e) => {
                                                    let calibrateNumber = 6;
                                                    if (this.props.config.ocrCalibration === 6 || this.props.config.ocrCalibration === 7) {
                                                        calibrateNumber = 7 * (e.target.value / 100);
                                                        //calibrateNumber = (this.props.config.ocrCalibration * (e.target.value/100));
                                                        //e.target.value > 100 ? onConfigChange('ocrCalibration', calibrateNumber) : onConfigChange('ocrCalibration', this.props.config.ocrCalibration);
                                                    } else {
                                                        calibrateNumber = 7 * (e.target.value / 100);
                                                        //e.target.value > 100 ? onConfigChange('ocrCalibration', calibrateNumber) : onConfigChange('ocrCalibration', 6);
                                                    }
                                                    //localStorage.setItem('ocrCalibration', calibrateNumber);
                                                    onConfigChange('ocrCalibration_internal', calibrateNumber);
                                                    onConfigChange('ocrScaling', e.target.value);
                                                }}
                                                style={{ width: '65px', marginRight: '15px' }}
                                            />
                                            <span className='label-text'>
                                                (用于
                                                <a
                                                    href='https://goto.ui.vision/x/idehelp?help=ocrdesktopscaling'
                                                    target='_blank'
                                                >
                                                    XClickTextRelative
                                                </a>
                                                校准)
                                            </span>
                                        </p>
                                    </div>

                                    <div className='row'>
                                        <p style={{ textAlign: 'right', marginTop: '10px' }}>
                                            <a onClick={() => this.showSettingsOfflineModal()}>高级：连接企业OCR服务器</a>
                                        </p>
                                    </div>
                                </>
                            ),
                        },
                        {
                            key: 'vision',
                            label: '视觉',
                            className: 'vision-pane',
                            children: (
                                <>
                                    <p>RPA IDE可以查看搜索网络浏览器内部网页和电脑桌面</p>
                                    <div className='row'>
                                        <Radio.Group value={this.props.config.cvScope}>
                                            <Radio
                                                value='browser'
                                                onClick={() => onConfigChange('cvScope', 'browser')}
                                            >
                                                网页自动化 (查看浏览器内部网页)
                                            </Radio>
                                            <Radio
                                                value='desktop'
                                                onClick={() => onConfigChange('cvScope', 'desktop')}
                                                disabled={!(this.state.xModuleData[getXDesktop().getName()] && this.state.xModuleData[getXDesktop().getName()].installed)}
                                            >
                                                <span>桌面自动化 (搜索完整桌面)</span>
                                                {this.state.xModuleData[getXDesktop().getName()] && this.state.xModuleData[getXDesktop().getName()].installed ? null : (
                                                    <a
                                                        target='_blank'
                                                        href={getXDesktop().downloadLink()}
                                                        style={{
                                                            marginLeft: '15px',
                                                        }}
                                                    >
                                                        需要首先安装XModule桌面自动化模块
                                                    </a>
                                                )}

                                                <div>
                                                    <Checkbox
                                                        onClick={(e) => onConfigChange('useDesktopScreenCapture', !e.target.checked)}
                                                        checked={this.props.config.useDesktopScreenCapture}
                                                        disabled={this.props.config.cvScope !== 'desktop' || !(this.state.xModuleData[getXScreenCapture().getName()] && this.state.xModuleData[getXScreenCapture().getName()].installed)}
                                                    >
                                                        <span>
                                                            使用原生
                                                            <a
                                                                href={getXScreenCapture().infoLink()}
                                                                target='_blank'
                                                            >
                                                                桌面屏幕捕获
                                                            </a>
                                                            (查看XModule介绍)
                                                        </span>
                                                        {this.state.xModuleData[getXScreenCapture().getName()] && this.state.xModuleData[getXScreenCapture().getName()].installed ? null : (
                                                            <a
                                                                target='_blank'
                                                                href={getXScreenCapture().downloadLink()}
                                                                style={{
                                                                    marginLeft: '15px',
                                                                }}
                                                            >
                                                                需要首先安装XModule屏幕捕获模块
                                                            </a>
                                                        )}
                                                    </Checkbox>
                                                </div>
                                            </Radio>
                                        </Radio.Group>
                                    </div>
                                    <p>
                                        在用例内，计算机视觉范围可以通过
                                        <a
                                            href={getXDesktop().infoLink()}
                                            target='_blank'
                                        >
                                            XDesktopAutomation
                                        </a>
                                        命令更改。此外，您可以使用
                                        <a
                                            // href='https://goto.ui.vision/x/idehelp?help=limitsearcharea'
                                            target='_blank'
                                        >
                                            visionLimitSearchArea
                                        </a>
                                        命令限制图像搜索区域
                                    </p>
                                    <div
                                        className='row'
                                        style={{ marginTop: '30px' }}
                                    >
                                        <p>默认视觉搜索置信度</p>
                                        <Select
                                            style={{ width: '200px' }}
                                            placeholder='间隔'
                                            value={'' + this.props.config.defaultVisionSearchConfidence}
                                            onChange={(val) => onConfigChange('defaultVisionSearchConfidence', parseFloat(val))}
                                        >
                                            {range(1, 11, 1).map((n) => (
                                                <Select.Option
                                                    key={n}
                                                    value={'' + (0.1 * n).toFixed(1)}
                                                >
                                                    {(0.1 * n).toFixed(1)}
                                                </Select.Option>
                                            ))}
                                        </Select>
                                    </div>

                                    <div
                                        className='row'
                                        style={{ marginTop: '30px' }}
                                    >
                                        <Checkbox
                                            onClick={(e) => onConfigChange('waitBeforeDesktopScreenCapture', !e.target.checked)}
                                            checked={this.props.config.waitBeforeDesktopScreenCapture}
                                        >
                                            <span>在截图之前等待</span>
                                            <Input
                                                type='number'
                                                min='0'
                                                max='60'
                                                value={this.props.config.secondsBeforeDesktopScreenCapture}
                                                style={{ width: '60px', margin: '0 10px' }}
                                                onChange={(e) => onConfigChange('secondsBeforeDesktopScreenCapture', Math.min(60, Number(e.target.value)))}
                                            />
                                            <span>秒, 适用需要切换窗口的场景</span>
                                        </Checkbox>
                                    </div>
                                </>
                            ),
                        },
                        {
                            key: 'ai',
                            label: 'AI',
                            className: 'ai-pane',
                            children: <AITab />,
                        },
                        {
                            key: 'xmodules',
                            label: 'XModules',
                            className: 'xmodules-pane',
                            children: (
                                <>
                                    <div className='xmodule-item'>
                                        <div className='xmodule-title'>
                                            <span>
                                                <b>XModule OCR模块</b> - Windows/Mac上的快速本地OCR
                                            </span>
                                            {/* <a
                                                href={getXLocal().infoLink()}
                                                target='_blank'
                                            >
                                                更多信息
                                            </a> */}
                                            <Button
                                                type='primary'
                                                onClick={() => {
                                                    getXLocal()
                                                        .getVersionLocal()
                                                        .then((data) => {
                                                            const { installed, version } = data;
                                                            const msg = installed ? `已安装 (v${version})` : '未安装';
                                                            message.info(`status updated: ${msg}`);

                                                            const p = !installed ? Promise.resolve() : getXLocal().initConfig();

                                                            p.catch((e) => {}).then(() => {
                                                                this.setState(
                                                                    updateIn(
                                                                        ['xModuleDataLocal', getXLocal().getName()],
                                                                        (orig) => ({
                                                                            ...orig,
                                                                            ...data,
                                                                            config: getXLocal().getCachedConfig(),
                                                                        }),
                                                                        this.state
                                                                    )
                                                                );
                                                            });
                                                        });
                                                }}
                                            >
                                                刷新状态
                                            </Button>
                                        </div>
                                        <div className='xmodule-status'>
                                            <label>状态:</label>

                                            {this.state.xModuleDataLocal[getXLocal().getName()] && this.state.xModuleDataLocal[getXLocal().getName()].installed ? (
                                                <div className='status-box'>
                                                    <span>
                                                        已安装 (v
                                                        {this.state.xModuleDataLocal[getXLocal().getName()].version})
                                                    </span>
                                                    <a
                                                        target='_blank'
                                                        href={getXLocal().checkUpdateLink(this.state.xModuleDataLocal[getXLocal().getName()] && this.state.xModuleDataLocal[getXLocal().getName()].version, Ext.runtime.getManifest().version)}
                                                    >
                                                        检查更新
                                                    </a>
                                                </div>
                                            ) : (
                                                <div className='status-box'>
                                                    <span>未安装</span>
                                                    <a
                                                        href={getXLocal().downloadLink()}
                                                        target='_blank'
                                                    >
                                                        下载
                                                    </a>
                                                </div>
                                            )}
                                        </div>
                                    </div>
                                    <div className='xmodule-item'>
                                        <div className='xmodule-title'>
                                            <span>
                                                <b>XModule文件访问模块</b> - 读写你的硬盘文件
                                            </span>
                                            {/* <a
                                                href={getXFile().infoLink()}
                                                target='_blank'
                                            >
                                                更多信息
                                            </a> */}
                                            <Button
                                                type='primary'
                                                onClick={() => {
                                                    getXFile()
                                                        .getVersion()
                                                        .then((data) => {
                                                            const { installed, version } = data;
                                                            const msg = installed ? `已安装 (v${version})` : '未安装';
                                                            message.info(`status updated: ${msg}`);

                                                            const p = !installed ? Promise.resolve() : getXFile().initConfig();

                                                            p.catch((e) => {}).then(() => {
                                                                this.setState(
                                                                    updateIn(
                                                                        ['xModuleData', getXFile().getName()],
                                                                        (orig) => ({
                                                                            ...orig,
                                                                            ...data,
                                                                            config: getXFile().getCachedConfig(),
                                                                        }),
                                                                        this.state
                                                                    )
                                                                );
                                                            });
                                                        });
                                                }}
                                            >
                                                刷新状态
                                            </Button>
                                        </div>

                                        <div className='xmodule-status'>
                                            <label>状态:</label>

                                            {this.state.xModuleData[getXFile().getName()] && this.state.xModuleData[getXFile().getName()].installed ? (
                                                <div className='status-box'>
                                                    <span>
                                                        已安装 (v
                                                        {this.state.xModuleData[getXFile().getName()].version})
                                                    </span>
                                                    <a
                                                        target='_blank'
                                                        href={getXFile().checkUpdateLink(this.state.xModuleData[getXFile().getName()] && this.state.xModuleData[getXFile().getName()].version, Ext.runtime.getManifest().version)}
                                                    >
                                                        检查更新
                                                    </a>
                                                </div>
                                            ) : (
                                                <div className='status-box'>
                                                    <span>未安装</span>
                                                    <a
                                                        href={getXFile().downloadLink()}
                                                        target='_blank'
                                                    >
                                                        下载
                                                    </a>
                                                </div>
                                            )}
                                        </div>

                                        <div className='xmodule-settings'>
                                            <h3>设置</h3>
                                            <div className='xmodule-settings-item'>
                                                <div className='settings-detail'>
                                                    <label>主文件夹</label>
                                                    <div className='settings-detail-content'>
                                                        <Input
                                                            type='text'
                                                            value={getXFile().getCachedConfig().rootDir}
                                                            disabled={!(this.state.xModuleData[getXFile().getName()] && this.state.xModuleData[getXFile().getName()].installed)}
                                                            onChange={(e) => {
                                                                const rootDir = e.target.value;

                                                                this.setState(compose(setIn(['xModuleData', getXFile().getName(), 'config', 'rootDir'], rootDir), setIn(['xFileRootDirChanged'], true))(this.state));

                                                                getXFile().setConfig({ rootDir });
                                                            }}
                                                            onBlur={() => {
                                                                if (this.state.xFileRootDirChanged) {
                                                                    this.setState({ xFileRootDirChanged: false });

                                                                    getXFile()
                                                                        .sanityCheck()
                                                                        .then(
                                                                            () => {
                                                                                this.setState(setIn(['xModuleData', getXFile().getName(), 'checkResult'], { error: null }, this.state));

                                                                                getStorageManager().emit(StorageManagerEvent.RootDirChanged);
                                                                            },
                                                                            (e) => {
                                                                                this.setState(setIn(['xModuleData', getXFile().getName(), 'checkResult'], { error: e.message }, this.state));

                                                                                this.props.updateUI({
                                                                                    showSettings: true,
                                                                                    settingsTab: 'xmodules',
                                                                                });
                                                                            }
                                                                        );
                                                                }
                                                            }}
                                                        />

                                                        {this.state.xModuleData[getXFile().getName()] && this.state.xModuleData[getXFile().getName()].checkResult && this.state.xModuleData[getXFile().getName()].checkResult.error ? <div className='check-result'>{this.state.xModuleData[getXFile().getName()].checkResult.error}</div> : null}
                                                    </div>
                                                </div>
                                                <div className='settings-desc'>在此文件夹中，RPA IDE 创建 /macros、/images、/testsuites、/datasources</div>
                                            </div>
                                        </div>
                                    </div>

                                    <div className='xmodule-item'>
                                        <div className='xmodule-title'>
                                            <span>
                                                <b>XModule模拟人工模块</b> - 使用操作系统原生事件进行单击/键入/拖动
                                            </span>
                                            {/* <a
                                                href={getXUserIO().infoLink()}
                                                target='_blank'
                                            >
                                                更多信息
                                            </a> */}
                                            <Button
                                                type='primary'
                                                onClick={() => {
                                                    getXUserIO()
                                                        .getVersion()
                                                        .then((data) => {
                                                            const { installed, version } = data;
                                                            const msg = installed ? `已安装 (v${version})` : '未安装';
                                                            message.info(`status updated: ${msg}`);

                                                            this.setState(
                                                                updateIn(
                                                                    ['xModuleData', getXUserIO().getName()],
                                                                    (orig) => ({
                                                                        ...orig,
                                                                        ...data,
                                                                        config: getXUserIO().getCachedConfig(),
                                                                    }),
                                                                    this.state
                                                                )
                                                            );
                                                        });
                                                }}
                                            >
                                                刷新状态
                                            </Button>
                                        </div>

                                        <div className='xmodule-status'>
                                            <label>状态:</label>

                                            {this.state.xModuleData[getXUserIO().getName()] && this.state.xModuleData[getXUserIO().getName()].installed ? (
                                                <div className='status-box'>
                                                    <span>
                                                        已安装 (v
                                                        {this.state.xModuleData[getXUserIO().getName()].version})
                                                    </span>
                                                    <a
                                                        target='_blank'
                                                        href={getXUserIO().checkUpdateLink(this.state.xModuleData[getXUserIO().getName()] && this.state.xModuleData[getXUserIO().getName()].version, Ext.runtime.getManifest().version)}
                                                    >
                                                        检查更新
                                                    </a>
                                                </div>
                                            ) : (
                                                <div className='status-box'>
                                                    <span>未安装</span>
                                                    <a
                                                        href={getXUserIO().downloadLink()}
                                                        target='_blank'
                                                    >
                                                        下载
                                                    </a>
                                                </div>
                                            )}
                                        </div>
                                    </div>

                                    <div className='xmodule-item'>
                                        <div className='xmodule-title'>
                                            <span>
                                                <b>XModule桌面自动化模块</b> - 可视化桌面自动化
                                            </span>
                                            {/* <a
                                                href={getXDesktop().infoLink()}
                                                target='_blank'
                                            >
                                                更多信息
                                            </a> */}
                                            <Button
                                                type='primary'
                                                onClick={() => {
                                                    getXDesktop()
                                                        .getVersion()
                                                        .then((data) => {
                                                            const { installed, version } = data;
                                                            const msg = installed ? `已安装 (v${version})` : '未安装';
                                                            message.info(`status updated: ${msg}`);

                                                            this.setState(
                                                                updateIn(
                                                                    ['xModuleData', getXDesktop().getName()],
                                                                    (orig) => ({
                                                                        ...orig,
                                                                        ...data,
                                                                        config: getXDesktop().getCachedConfig(),
                                                                    }),
                                                                    this.state
                                                                )
                                                            );
                                                        });
                                                }}
                                            >
                                                刷新状态
                                            </Button>
                                        </div>

                                        <div className='xmodule-status'>
                                            <label>状态:</label>

                                            {this.state.xModuleData[getXDesktop().getName()] && this.state.xModuleData[getXDesktop().getName()].installed ? (
                                                <div className='status-box'>
                                                    <span>
                                                        已安装 (v
                                                        {this.state.xModuleData[getXDesktop().getName()].version})
                                                    </span>
                                                    <a
                                                        target='_blank'
                                                        href={getXDesktop().checkUpdateLink(this.state.xModuleData[getXDesktop().getName()] && this.state.xModuleData[getXDesktop().getName()].version, Ext.runtime.getManifest().version)}
                                                    >
                                                        检查更新
                                                    </a>
                                                </div>
                                            ) : (
                                                <div className='status-box'>
                                                    <span>未安装</span>
                                                    <a
                                                        href={getXDesktop().downloadLink()}
                                                        target='_blank'
                                                    >
                                                        下载
                                                    </a>
                                                </div>
                                            )}
                                        </div>
                                    </div>

                                    <div className='xmodule-item'>
                                        <div className='xmodule-title'>
                                            <span>
                                                <b>XModule捕获屏幕模块</b> - 更快地选择图像
                                            </span>
                                            {/* <a
                                                 href={getXScreenCapture().infoLink()}
                                                target='_blank'
                                            >
                                                更多信息
                                            </a> */}
                                            <Button
                                                type='primary'
                                                onClick={() => {
                                                    getXScreenCapture()
                                                        .getVersion()
                                                        .then((data) => {
                                                            const { installed, version } = data;
                                                            const msg = installed ? `已安装 (v${version})` : '未安装';
                                                            message.info(`status updated: ${msg}`);

                                                            this.setState(
                                                                updateIn(
                                                                    ['xModuleData', getXScreenCapture().getName()],
                                                                    (orig) => ({
                                                                        ...orig,
                                                                        ...data,
                                                                        config: getXScreenCapture().getCachedConfig(),
                                                                    }),
                                                                    this.state
                                                                )
                                                            );
                                                        });
                                                }}
                                            >
                                                刷新状态
                                            </Button>
                                        </div>

                                        <div className='xmodule-status'>
                                            <label>状态:</label>

                                            {this.state.xModuleData[getXScreenCapture().getName()] && this.state.xModuleData[getXScreenCapture().getName()].installed ? (
                                                <div className='status-box'>
                                                    <span>
                                                        已安装 (v
                                                        {this.state.xModuleData[getXScreenCapture().getName()].version})
                                                    </span>
                                                    <a
                                                        target='_blank'
                                                        href={getXScreenCapture().checkUpdateLink(this.state.xModuleData[getXScreenCapture().getName()] && this.state.xModuleData[getXScreenCapture().getName()].version, Ext.runtime.getManifest().version)}
                                                    >
                                                        检查更新
                                                    </a>
                                                </div>
                                            ) : (
                                                <div className='status-box'>
                                                    <span>未安装</span>
                                                    <a
                                                        href={getXScreenCapture().downloadLink()}
                                                        target='_blank'
                                                    >
                                                        下载
                                                    </a>
                                                </div>
                                            )}
                                        </div>
                                    </div>
                                </>
                            ),
                        },
                        {
                            key: 'backup',
                            label: '备份',
                            className: 'backup-pane',
                            children: (
                                <>
                                    <h4>自动备份</h4>
                                    <p>自动备份提醒可帮助您定期将用例和其他数据导出为ZIP存档。作为浏览器扩展，IDE必须将其数据存储在浏览器扩展中。这意味着卸载扩展程序时，数据也会被删除。因此，有备份是件好事！ 请注意，如果使用了XModule文件访问模块的的硬盘存储模式，则备份存档将包含这些文件。</p>
                                    <div className='row'>
                                        <Checkbox
                                            onClick={(e) => onConfigChange('enableAutoBackup', !e.target.checked)}
                                            checked={this.props.config.enableAutoBackup}
                                        />
                                        <span>每</span>
                                        <Input
                                            type='number'
                                            min={1}
                                            disabled={!this.props.config.enableAutoBackup}
                                            value={this.props.config.autoBackupInterval}
                                            onChange={(e) => onConfigChange('autoBackupInterval', e.target.value)}
                                            style={{ width: '60px' }}
                                        />
                                        <span>天显示备份提醒</span>
                                    </div>
                                    <div className='row'>
                                        <p>
                                            备份包括
                                            <span style={{ fontWeight: 'bold' }}>用例、图像和视觉文件</span>.
                                        </p>
                                    </div>
                                    <div className='row'>
                                        <Button
                                            type='primary'
                                            onClick={() => this.props.runBackup()}
                                        >
                                            立即运行备份
                                        </Button>
                                        <span>{'  '}立即创建ZIP备份文件。</span>
                                    </div>
                                    <div
                                        style={{ paddingTop: '30px' }}
                                        className='row'
                                    >
                                        <Button
                                            type='primary'
                                            onClick={() => {
                                                const $input = document.getElementById('select_zip_file');

                                                if ($input) {
                                                    $input.click();
                                                }
                                            }}
                                        >
                                            从备份还原数据
                                        </Button>
                                        <span>
                                            {'  '} 选择一个ZIP备份文件进行导入
                                            {/* (
                                            <a
                                                href='https://goto.ui.vision/x/idehelp?help=bkup_import'
                                                target='_blank'
                                            >
                                                更多信息
                                            </a>
                                            ) */}
                                        </span>

                                        <input
                                            type='file'
                                            accept='.zip'
                                            id='select_zip_file'
                                            ref={(ref) => {
                                                this.zipFileInput = ref;
                                            }}
                                            style={{ display: 'none' }}
                                            onChange={(e) => {
                                                setTimeout(() => {
                                                    this.zipFileInput.value = null;
                                                }, 500);

                                                const file = e.target.files[0];

                                                restoreBackup({
                                                    file,
                                                    storage: getStorageManager().getCurrentStrategyType(),
                                                }).then(
                                                    (result) => {
                                                        getStorageManager().emit(StorageManagerEvent.ForceReload);
                                                        message.success('Backup restored');

                                                        this.props.addLog('info', ['Backup restored:', `${result.count.macro} macros`, `${result.count.testSuite} test suites`, `${result.count.csv} csvs`, `${result.count.screenshot} screenshots`, `${result.count.vision} vision images`].join('\n'));
                                                    },
                                                    (e) => {
                                                        message.error('Failed to restore: ' + e.message);
                                                        console.error(e);
                                                    }
                                                );
                                            }}
                                        />
                                    </div>
                                </>
                            ),
                        },
                        {
                            key: 'security',
                            label: '安全',
                            className: 'security-pane',
                            children: (
                                <>
                                    <h4>Master password for Password Encryption</h4>
                                    <p>
                                        A master password is used to encrypt and decrypt all stored website passwords. The websites passwords are encrypted using strong encryption.&nbsp;&nbsp;
                                        {/* <a
                                            target='_blank'
                                            href='https://goto.ui.vision/x/idehelp?help=encryption'
                                        >
                                            更多信息 &gt;&gt;
                                        </a> */}
                                    </p>
                                    <div>
                                        <Radio.Group value={this.props.config.shouldEncryptPassword}>
                                            <Radio
                                                value='no'
                                                onClick={() => onConfigChange('shouldEncryptPassword', 'no')}
                                            >
                                                不加密密码
                                            </Radio>
                                            <Radio
                                                value='master_password'
                                                onClick={() => onConfigChange('shouldEncryptPassword', 'master_password')}
                                            >
                                                在此处输入主密码进行存储
                                            </Radio>
                                        </Radio.Group>

                                        {this.props.config.shouldEncryptPassword === 'master_password' ? (
                                            <div>
                                                <div>
                                                    <label>Master password:</label>
                                                    <Input
                                                        type='password'
                                                        style={{ width: '200px' }}
                                                        value={this.props.config.masterPassword}
                                                        onChange={(e) => onConfigChange('masterPassword', e.target.value)}
                                                    />
                                                </div>
                                                <div>
                                                    <hr style={{ margin: '20px 0' }} />
                                                    <h4>创建加密文本字符串</h4>
                                                    <p>The feature uses the master password to encrypt text. The encrypted string can be used with TYPE, SENDKEY and XTYPE.</p>
                                                    <div className='input-line'>
                                                        <span className='input-label'>Text to encrypt:</span>
                                                        <Input
                                                            type={this.state.showText ? 'text' : 'password'}
                                                            style={{ width: '200px' }}
                                                            value={this.state.textToEncrypt}
                                                            onChange={(e) => {
                                                                this.setState({
                                                                    textToEncrypt: e.target.value,
                                                                    encryptedText: '',
                                                                });
                                                            }}
                                                        />
                                                        <Checkbox
                                                            onClick={(e) => {
                                                                this.setState({ showText: !e.target.checked });
                                                            }}
                                                            checked={this.state.showText}
                                                        >
                                                            显示文本
                                                        </Checkbox>
                                                    </div>
                                                    <div className='input-line'>
                                                        <span className='input-label'>加密字符串：</span>
                                                        <Input
                                                            readOnly={true}
                                                            type='text'
                                                            style={{ width: '200px' }}
                                                            value={this.state.encryptedText}
                                                        />
                                                    </div>
                                                    <div className='input-line'>
                                                        <span className='input-label'></span>
                                                        <Button
                                                            type='primary'
                                                            onClick={() => {
                                                                encrypt(this.state.textToEncrypt).then((text) => {
                                                                    this.setState({ encryptedText: text });

                                                                    copyToClipboard(text, {
                                                                        format: 'text/plain',
                                                                    });

                                                                    message.success('Copied to clipboard');
                                                                });
                                                            }}
                                                        >
                                                            Encrypt &amp; Copy
                                                        </Button>

                                                        {/* <a
                                                            href='https://goto.ui.vision/x/idehelp?help=encrypt'
                                                            target='_blank'
                                                        >
                                                            (更多信息)
                                                        </a> */}
                                                    </div>
                                                </div>
                                            </div>
                                        ) : null}
                                    </div>
                                </>
                            ),
                        },
                        {
                            key: 'selenium',
                            label: '代理配置',
                            className: 'selenium-pane',
                            children: (
                                <>
                                    <h4>代理选项</h4>

                                    <Form>
                                        <Form.Item
                                            label='默认代理 (IP:Port)'
                                            {...displayConfig}
                                        >
                                            <Input
                                                type='text'
                                                style={{ width: '300px' }}
                                                value={this.props.config.defaultProxy}
                                                onChange={(e) => onConfigChange('defaultProxy', e.target.value)}
                                                placeholder='例如： http://0.0.0.0:1234'
                                            />
                                        </Form.Item>
                                        <Form.Item
                                            label='用户名、密码'
                                            {...displayConfig}
                                        >
                                            <Input
                                                type='text'
                                                style={{ width: '300px' }}
                                                value={this.props.config.defaultProxyAuth}
                                                onChange={(e) => onConfigChange('defaultProxyAuth', e.target.value)}
                                                placeholder='例如： admin, mypassword'
                                            />
                                        </Form.Item>
                                        <Form.Item
                                            label='状态'
                                            {...displayConfig}
                                        >
                                            <Radio.Group value={this.props.proxy ? 'on' : 'off'}>
                                                <Radio
                                                    value='on'
                                                    onClick={() => onChangeProxyStatus('on')}
                                                >
                                                    代理打开
                                                </Radio>
                                                <Radio
                                                    value='off'
                                                    onClick={() => onChangeProxyStatus('off')}
                                                >
                                                    代理关闭
                                                </Radio>
                                            </Radio.Group>

                                            <Checkbox
                                                onClick={(e) => onConfigChange('turnOffProxyAfterReplay', !e.target.checked)}
                                                checked={this.props.config.turnOffProxyAfterReplay}
                                                style={{ marginTop: '10px' }}
                                            >
                                                允许回放结束时关闭 (代理由
                                                <a
                                                    href='https://goto.ui.vision/x/idehelp?cmd=setproxy'
                                                    target='_blank'
                                                >
                                                    setProxy
                                                </a>
                                                命令控制 )
                                            </Checkbox>
                                        </Form.Item>
                                    </Form>
                                </>
                            ),
                        },
                        // {
                        //     key: 'register',
                        //     label: '专业版|企业版',
                        //     className: 'register-pane',
                        //     children: (
                        //         <>
                        //             <div
                        //                 className={cn('register-note', {
                        //                     inactive: !getLicenseService().hasNoLicense(),
                        //                 })}
                        //             >
                        //                 <p>RPA IDE专业版和企业版给需要企业功能的用户使用，提供包括直接文件存储、更新管理和优先支持服务。如果您已经获得了专业版或企业版的许可证密钥，请在下面输入：</p>
                        //                 <div className='actions'>
                        //                     <a
                        //                         href={getLicenseService().getUpgradeUrl()}
                        //                         target='_blank'
                        //                     >
                        //                         单击此处升级
                        //                     </a>
                        //                 </div>
                        //             </div>
                        //             <div className='register-form'>
                        //                 <label>输入许可证密钥:</label>
                        //                 <div className='register-row'>
                        //                     <Input
                        //                         value={this.state.registerKey}
                        //                         type='text'
                        //                         onChange={(e) => {
                        //                             this.setState({
                        //                                 registerKey: e.target.value,
                        //                             });
                        //                         }}
                        //                     />
                        //                     <Button
                        //                         type='primary'
                        //                         loading={this.state.isCheckingLicense}
                        //                         onClick={this.checkRegisterKey}
                        //                     >
                        //                         检查 Key
                        //                     </Button>
                        //                 </div>
                        //             </div>
                        //             <div className='register-status'>
                        //                 {getLicenseService().hasNoLicense() ? (
                        //                     <div>
                        //                         <span>许可状态：</span>
                        //                         <b>{this.isEitherXModuleInstalled() ? getLicenseService().getEditionName() + ' 已安装' : '未安装'}</b>.
                        //                         <a
                        //                             href={getLicenseService().getUpgradeUrl()}
                        //                             target='_blank'
                        //                         >
                        //                             升级到RPA IDE专业版或企业版
                        //                         </a>
                        //                     </div>
                        //                 ) : null}

                        //                 {getLicenseService().isPersonalLicense() ? (
                        //                     <div>
                        //                         XModules状态: <b>{getLicenseService().getEditionName()}</b>.
                        //                         <a
                        //                             href={getLicenseService().getUpgradeUrl()}
                        //                             target='_blank'
                        //                         >
                        //                             升级到专业版或企业版
                        //                         </a>
                        //                     </div>
                        //                 ) : null}

                        //                 {getLicenseService().isProLicense() ? (
                        //                     <div>
                        //                         XModules状态: <b>使用的是 {getLicenseService().getEditionName()} 版本</b>.
                        //                         <a
                        //                             href={getLicenseService().getUpgradeUrl()}
                        //                             target='_blank'
                        //                         >
                        //                             联系技术支持
                        //                         </a>
                        //                     </div>
                        //                 ) : null}

                        //                 {getLicenseService().isPlayerLicense() ? (
                        //                     <div>
                        //                         XModules状态: <b>使用的是 {getLicenseService().getEditionName()} 版本</b>.
                        //                         <a
                        //                             href={getLicenseService().getUpgradeUrl()}
                        //                             target='_blank'
                        //                         >
                        //                             联系技术支持
                        //                         </a>
                        //                     </div>
                        //                 ) : null}
                        //             </div>
                        //         </>
                        //     ),
                        // },
                    ]}
                ></Tabs>
            </Modal>
        );
    }

    renderStatus() {
        const { status, player } = this.props;
        const renderInner = () => {
            switch (status) {
                case C.APP_STATUS.RECORDER:
                    return 'Recording';

                case C.APP_STATUS.PLAYER: {
                    switch (player.status) {
                        case C.PLAYER_STATUS.PLAYING: {
                            const { nextCommandIndex, loops, currentLoop, timeoutStatus } = player;

                            if (nextCommandIndex === null || loops === null || currentLoop === 0) {
                                return '';
                            }

                            const parts = [`行 ${nextCommandIndex + 1}`, `轮次 ${currentLoop}/${loops}`];

                            if (timeoutStatus && timeoutStatus.type && timeoutStatus.total) {
                                const { type, total, past } = timeoutStatus;
                                parts.unshift(`${type} ${past / 1000}s (${total / 1000})`);
                            }

                            return parts.join(' | ');
                        }

                        case C.PLAYER_STATUS.PAUSED:
                            return '运行暂停';

                        default:
                            return '';
                    }
                }

                default:
                    return '';
            }
        };

        return <div className='status'>{renderInner()}</div>;
    }

    renderActions() {
        const { player, status } = this.props;

        const onClickMenuItem = ({ key }) => {
            switch (key) {
                case 'play_loop': {
                    this.togglePlayLoopsModal(true);
                    break;
                }
            }
        };

        // const playMenu = (
        //   <Menu onClick={onClickMenuItem} selectable={false}>
        //     <Menu.Item key="play_loop" disabled={false}>
        //       Play loop..
        //     </Menu.Item>
        //   </Menu>
        // );

        if (status === C.APP_STATUS.RECORDER) {
            return (
                <div className='actions'>
                    <Button
                        onClick={this.onToggleRecord}
                        style={{ color: '#ff0000' }}
                    >
                        <span>停止录制</span>
                    </Button>
                </div>
            );
        }

        switch (player.status) {
            case C.PLAYER_STATUS.PLAYING: {
                return (
                    <div className='actions'>
                        <Button.Group>
                            <Button onClick={() => this.getPlayer().stop()}>
                                <span>停止</span>
                            </Button>
                            <Button onClick={() => this.getPlayer('testCase').pause()}>
                                <span>暂停</span>
                            </Button>
                        </Button.Group>
                    </div>
                );
            }

            case C.PLAYER_STATUS.PAUSED: {
                return (
                    <div className='actions'>
                        <Button.Group>
                            {this.props.player.mode === C.PLAYER_MODE.TEST_CASE ? <Button onClick={() => this.getPlayer('testCase').resume(true)}>单步运行</Button> : null}
                            <Button onClick={() => this.getPlayer().stop()}>停止</Button>
                            <Button onClick={() => this.getPlayer('testCase').resume()}>继续</Button>
                        </Button.Group>
                    </div>
                );
            }

            case C.PLAYER_STATUS.STOPPED: {
                return (
                    <div className='actions'>
                        <Button
                            disabled={!getLicenseService().canPerform(Feature.Record)}
                            onClick={this.onToggleRecord}
                        >
                            <span>录制</span>
                        </Button>{' '}
                        <Button onClick={() => this.playCurrentMacro(true)}>单步运行</Button>
                        <Button.Group className='play-actions'>
                            <Dropdown.Button
                                onClick={() => this.playCurrentMacro(false)}
                                menu={{
                                    items: [
                                        {
                                            key: 'play_loop',
                                            label: '循环运行',
                                            disabled: false,
                                        },
                                    ],
                                    onClick: onClickMenuItem,
                                    selectable: false,
                                    trigger: ['click'],
                                }}
                            >
                                <span>运行用例</span>
                            </Dropdown.Button>
                        </Button.Group>
                        {/* <Button onClick={async() => {
              const tabId = await getActiveTabId();
              alert(`TabId: ${tabId}`);
            }}>
              TabId
            </Button> */}
                        <Button
                            shape='circle'
                            onClick={() => this.showSettingsModal()}
                        >
                            <SettingOutlined />
                        </Button>
                    </div>
                );
            }
        }
    }

    renderMacro() {
        const { editing, player, hasUnsaved } = this.props;
        const { src } = editing.meta;
        const isPlayerStopped = player.status === C.PLAYER_STATUS.STOPPED;
        const klass = hasUnsaved ? 'unsaved' : '';

        const saveBtnState = {
            text: src ? '保存' : '保存..',
            disabled: !hasUnsaved,
        };

        return (
            <div className='select-case'>
                <span
                    title={src ? src.name : '无标题'}
                    className={'test-case-name ' + klass}
                >
                    {src ? src.name : '无标题'}
                </span>

                {!isPlayerStopped ? null : (
                    <Button
                        disabled={saveBtnState.disabled}
                        onClick={this.onClickSave}
                    >
                        <span>{saveBtnState.text}</span>
                    </Button>
                )}
            </div>
        );
    }

    render() {
        const { player } = this.props;
        const isPlayerStopped = player.status === C.PLAYER_STATUS.STOPPED;

        return (
            <div className={'header ' + this.props.status.toLowerCase()}>
                {this.renderMacro()}
                {this.renderStatus()}
                {this.renderActions()}
                {this.renderPlayLoopModal()}
                {this.renderSettingModal()}
                {this.renderPublicWebsiteWhiteList()}
                {this.renderSettingOfflineModal()}
            </div>
        );
    }
}

export default connect(
    (state) => ({
        hasUnsaved: hasUnsavedMacro(state),
        route: state.route,
        editing: state.editor.editing,
        player: state.player,
        status: state.status,
        config: state.config,
        ui: state.ui,
        proxy: state.proxy,
    }),
    (dispatch) => bindActionCreators({ ...actions, ...simpleActions }, dispatch)
)(withRouter(Header));
